// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
/* Copyright (c) 2021~2022 Hengqi Chen */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "sigsnoop.h"

#define MAX_ENTRIES 10240

const volatile pid_t filtered_pid = 0;
const volatile int target_signal = 0;
const volatile bool failed_only = false;

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, MAX_ENTRIES);
    __type(key, __u32);
    __type(value, struct event);
} values SEC(".maps");

struct {
    __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
    __uint(key_size, sizeof(__u32));
    __uint(value_size, sizeof(__u32));
} events SEC(".maps");

static int probe_entry(pid_t tpid, int sig) {
    struct event event = {};
    __u64 pid_tgid;
    __u32 pid, tid;

    if (target_signal && sig != target_signal)
        return 0;

    pid_tgid = bpf_get_current_pid_tgid();
    pid = pid_tgid >> 32;
    tid = (__u32)pid_tgid;
    if (filtered_pid && pid != filtered_pid)
        return 0;

    event.pid = pid;
    event.tpid = tpid;
    event.sig = sig;
    bpf_get_current_comm(event.comm, sizeof(event.comm));
    bpf_map_update_elem(&values, &tid, &event, BPF_ANY);
    return 0;
}

static int probe_exit(void* ctx, int ret) {
    __u64 pid_tgid = bpf_get_current_pid_tgid();
    __u32 tid = (__u32)pid_tgid;
    struct event* eventp;

    eventp = bpf_map_lookup_elem(&values, &tid);
    if (!eventp)
        return 0;

    if (failed_only && ret >= 0)
        goto cleanup;

    eventp->ret = ret;
    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, eventp,
                          sizeof(*eventp));

cleanup:
    bpf_map_delete_elem(&values, &tid);
    return 0;
}

SEC("tracepoint/syscalls/sys_enter_kill")
int kill_entry(struct trace_event_raw_sys_enter* ctx) {
    pid_t tpid = (pid_t)ctx->args[0];
    int sig = (int)ctx->args[1];

    return probe_entry(tpid, sig);
}

SEC("tracepoint/syscalls/sys_exit_kill")
int kill_exit(struct trace_event_raw_sys_exit* ctx) {
    return probe_exit(ctx, ctx->ret);
}

SEC("tracepoint/syscalls/sys_enter_tkill")
int tkill_entry(struct trace_event_raw_sys_enter* ctx) {
    pid_t tpid = (pid_t)ctx->args[0];
    int sig = (int)ctx->args[1];

    return probe_entry(tpid, sig);
}

SEC("tracepoint/syscalls/sys_exit_tkill")
int tkill_exit(struct trace_event_raw_sys_exit* ctx) {
    return probe_exit(ctx, ctx->ret);
}

SEC("tracepoint/syscalls/sys_enter_tgkill")
int tgkill_entry(struct trace_event_raw_sys_enter* ctx) {
    pid_t tpid = (pid_t)ctx->args[1];
    int sig = (int)ctx->args[2];

    return probe_entry(tpid, sig);
}

SEC("tracepoint/syscalls/sys_exit_tgkill")
int tgkill_exit(struct trace_event_raw_sys_exit* ctx) {
    return probe_exit(ctx, ctx->ret);
}

SEC("tracepoint/signal/signal_generate")
int sig_trace(struct trace_event_raw_signal_generate* ctx) {
    struct event event = {};
    pid_t tpid = ctx->pid;
    int ret = ctx->errno;
    int sig = ctx->sig;
    __u64 pid_tgid;
    __u32 pid;

    if (failed_only && ret == 0)
        return 0;

    if (target_signal && sig != target_signal)
        return 0;

    pid_tgid = bpf_get_current_pid_tgid();
    pid = pid_tgid >> 32;
    if (filtered_pid && pid != filtered_pid)
        return 0;

    event.pid = pid;
    event.tpid = tpid;
    event.sig = sig;
    event.ret = ret;
    bpf_get_current_comm(event.comm, sizeof(event.comm));
    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event,
                          sizeof(event));
    return 0;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";
